/**
 * clue服务实现
 *
 * @author sxf
 * @email sxf02615@163.com
 * @date 2025/1/15
 */
package com.sxf.crm.service.impl;


import com.sxf.crm.dto.*;
import com.sxf.crm.entity.Clue;
import com.sxf.crm.entity.ClueFollowUp;
import com.sxf.crm.entity.Customer;
import com.sxf.crm.entity.CustomerDepartment;
import com.sxf.crm.entity.Opportunity;
import com.sxf.crm.entity.User;
import com.sxf.crm.enums.ClueSource;
import com.sxf.crm.enums.ClueStatus;
import com.sxf.crm.enums.CustomerSource;
import com.sxf.crm.enums.CustomerStage;
import com.sxf.crm.enums.CustomerStatus;
import com.sxf.crm.enums.OpportunitySource;
import com.sxf.crm.enums.OpportunityStatus;
import com.sxf.crm.enums.ClueConversionType;
import com.sxf.crm.enums.CluePriority;
import com.sxf.crm.repository.ClueRepository;
import com.sxf.crm.service.ClueService;
import com.sxf.crm.service.ClueFollowUpService;
import com.sxf.crm.service.ClueLogService;
import com.sxf.crm.service.CustomerDepartmentService;
import com.sxf.crm.service.CustomerService;
import com.sxf.crm.service.DepartmentService;
import com.sxf.crm.service.OpportunityService;
import com.sxf.crm.service.UserService;

import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@Transactional
public class ClueServiceImpl implements ClueService {
    
    private final UserService userService; 
    
    private final CustomerService customerService;

    private final DepartmentService departmentService;

    private final CustomerDepartmentService customerDepartmentService;

    private final ClueRepository clueRepository;

    private final ClueLogService clueLogService;

    private final ClueFollowUpService clueFollowUpService;

    private final OpportunityService opportunityService;
    
    @Override
    public Page<ClueDTO> listClues(String name, ClueSource source, ClueStatus status, CluePriority priority, Pageable pageable) {
        Page<Clue> page = clueRepository.findByConditions(name, source, status, priority, pageable);
        return page.map(this::convertToDTO);
    }
    
    @Override
    public ClueDTO createClue(ClueRequest request,Long currentUserId) {
        Clue clue = new Clue();
        BeanUtils.copyProperties(request, clue);
        clue.setStatus(ClueStatus.NEW);
        clue.setCreateAt(System.currentTimeMillis());
        clue.setRecycled(false);
        clue.setCreateUserId(currentUserId);
        clue = clueRepository.save(clue);
        
        User operator = userService.getUserById(currentUserId);
        clueLogService.logCreate(clue, operator);
        
        return convertToDTO(clue);
    }
    
    @Override
    public ClueDTO updateClue(Long id, ClueRequest request, Long currentUserId) {
        Clue clue = clueRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("线索不存在"));
                
        String statusBefore = clue.getStatus().getLabel();
        BeanUtils.copyProperties(request, clue, "id", "createTime");
        clue.setUpdateAt(System.currentTimeMillis());
        clue.setUpdateUserId(currentUserId);
        clue = clueRepository.save(clue);
        
        User operator = userService.getUserById(currentUserId);
        clueLogService.logUpdate(clue, operator, "更新基本信息");
        
        // 如果状态发生变化，记录状态变更日志
        if (!statusBefore.equals(clue.getStatus().getLabel())) {
            clueLogService.logStatusChange(clue, operator, statusBefore, clue.getStatus().getLabel());
        }
        
        return convertToDTO(clue);
    }
    
    @Override
    public void deleteClue(Long id,Long currentUserId) {
        Clue clue = clueRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("线索不存在"));
        
        clue.setStatus(ClueStatus.DELETED);
        clue.setUpdateAt(System.currentTimeMillis());
        clue.setUpdateUserId(currentUserId);
        clueRepository.save(clue);
    }
    
    @Override
    public ClueDTO getClue(Long id) {
        Clue clue = clueRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("线索不存在"));
        return convertToDTO(clue);
    }
    
    @Override
    @Transactional
    public void assignClue(Long clueId, Long ownerId, Long currentUserId) {
        Clue clue = clueRepository.findById(clueId)
                .orElseThrow(() -> new RuntimeException("线索不存在"));
                
        String statusBefore = clue.getStatus().getLabel();
        clue.setOwnerId(ownerId);
        clue.setAssignerId(currentUserId);
        clue.setAssignTime(System.currentTimeMillis());
        clue.setStatus(ClueStatus.ASSIGNED);
        clue.setUpdateAt(System.currentTimeMillis());
        clue.setUpdateUserId(currentUserId);
        
        clue = clueRepository.save(clue);
        
        User operator = userService.getUserById(currentUserId);
        User owner = userService.getUserById(ownerId);
        clueLogService.logAssign(clue, operator, owner);
        
        if (!statusBefore.equals(clue.getStatus().getLabel())) {
            clueLogService.logStatusChange(clue, operator, statusBefore, clue.getStatus().getLabel());
        }
    }
    
    @Override
    public void claimClue(Long clueId,Long currentUserId) {
        Clue clue = clueRepository.findById(clueId)
                .orElseThrow(() -> new RuntimeException("线索不存在"));
        
        if (clue.getOwnerId() != null) {
            throw new RuntimeException("线索已被分配，无法领取");
        }
        
        String statusBefore = clue.getStatus().getLabel();
        clue.setOwnerId(currentUserId);
        clue.setStatus(ClueStatus.RECEIVED);
        clue.setUpdateAt(System.currentTimeMillis());
        clue.setUpdateUserId(currentUserId);
        clue = clueRepository.save(clue);
        
        User operator = userService.getUserById(currentUserId);
        clueLogService.logClaim(clue, operator);
        
        if (!statusBefore.equals(clue.getStatus().getLabel())) {
            clueLogService.logStatusChange(clue, operator, statusBefore, clue.getStatus().getLabel());
        }
    }
    
    @Override
    public void recycleClue(Long clueId,Long currentUserId,ClueRecycleRequest request) {
        Clue clue = clueRepository.findById(clueId)
                .orElseThrow(() -> new RuntimeException("线索不存在"));
        
        if(clue.getRecycled()){
            throw new RuntimeException("线索已回收，无法再次回收");
        }
        
        String statusBefore = clue.getStatus().getLabel();
        clue.setRecycled(true);
        clue.setOwnerId(null);
        clue.setAssignerId(null);
        clue.setAssignTime(null);
        clue.setStatus(ClueStatus.RECYCLED);
        clue.setUpdateAt(System.currentTimeMillis());
        clue.setUpdateUserId(currentUserId);
        clue.setRecycleUserId(currentUserId );
        clue.setRecycleTime(System.currentTimeMillis());
        clue.setRecycleReason(request.getRecycleReason());
        clue = clueRepository.save(clue);
        
        User operator = userService.getUserById(currentUserId);
        clueLogService.logRecycle(clue, operator);
        
        clueLogService.logStatusChange(clue, operator, statusBefore, clue.getStatus().getLabel());

    }
    
    @Override
    @Transactional
    public void convertClue(Long clueId, ClueConvertRequest request,Long currentUserId) {
        Clue clue = clueRepository.findById(clueId)
                .orElseThrow(() -> new RuntimeException("线索不存在"));
                
        String statusBefore = clue.getStatus().getLabel();
        
        clue.setConvertType(request.getConvertType());
        clue.setConvertReason(request.getConvertReason());
        clue.setConvertTime(System.currentTimeMillis());
        clue.setConvertUserId(currentUserId);

        clue.setStatus(ClueStatus.CONVERTED);
        clue.setUpdateAt(System.currentTimeMillis());
        clue.setUpdateUserId(currentUserId);

        if (request.getConvertType().equals(ClueConversionType.CUSTOMER.name())) {
            CustomerDTO customer = new CustomerDTO();
            BeanUtils.copyProperties(request, customer);
            customer.setCreateAt(System.currentTimeMillis());
            customer.setCreateUserId(currentUserId);
            customer.setStage(CustomerStage.LEAD);
            customer.setStatus(CustomerStatus.PENDING);
            customer.setSource(CustomerSource.CLUE);
            customer.setName(request.getCustomerName());
            customer.setAddress(clue.getPosition());

            customer = customerService.createCustomer(customer);

            clue.setCustomerName(customer.getName());
            clue.setCustomerId(customer.getId());

            CustomerDepartment customerDepartment = new CustomerDepartment();
            customerDepartment.setCustomer(customerService.findById(customer.getId()));
            customerDepartment.setDepartment(departmentService.findById(request.getDepartmentId()));
            customerDepartment.setSales(userService.getUserById(request.getSalesId()));
            customerDepartment.setCreateAt(System.currentTimeMillis());
            customerDepartment.setCreateUserId(currentUserId);
            customerDepartmentService.create(customerDepartment);

        } else if (request.getConvertType().equals(ClueConversionType.OPPORTUNITY.name())) {
            Opportunity opportunity = new Opportunity();
            Customer customer = customerService.findById(request.getCustomerId());
            BeanUtils.copyProperties(request, opportunity);
            opportunity.setName(customer.getName());
            opportunity.setCreateAt(System.currentTimeMillis());
            opportunity.setCreateUserId(currentUserId);
            opportunity.setCustomer(customer);
            opportunity.setSource(OpportunitySource.CLUE);
            opportunity.setStatus(OpportunityStatus.UNASSIGNED);
            opportunity = opportunityService.create(opportunity, currentUserId);

            clue.setOpportunityName(opportunity.getName());
            clue.setOpportunityId(opportunity.getId());
        }
        
        clue = clueRepository.save(clue);
        
        User operator = userService.getUserById(currentUserId);
        clueLogService.logConvert(clue, operator, request.getConvertType(), request.getConvertReason());
        clueLogService.logStatusChange(clue, operator, statusBefore, clue.getStatus().getLabel());
    }
    
    @Override
    public List<ClueDTO> getUserClues(Long userId) {
        List<Clue> clues = clueRepository.findByOwnerId(userId);
        return clues.stream().map(this::convertToDTO).collect(Collectors.toList());
    }
    
    @Override
    public List<ClueDTO> getUnassignedClues() {
        List<Clue> clues = clueRepository.findByOwnerIdIsNull();
        return clues.stream().map(this::convertToDTO).collect(Collectors.toList());
    }
    
    @Override
    public void autoRecycleStaleClues() {
        // 回收30天未跟进的线索
        LocalDateTime cutoffTime = LocalDateTime.now().minusDays(30);
        List<Clue> staleClues = clueRepository.findStaleClues(cutoffTime);
        
        for (Clue clue : staleClues) {
            if (!clue.getRecycled()) {
                clue.setRecycled(true);
                clue.setOwnerId(null);
                clue.setStatus(ClueStatus.AUTO_RECYCLED);
                clue.setUpdateAt(System.currentTimeMillis());
            }
        }
        
        clueRepository.saveAll(staleClues);
        
        // 记录自动回收的日志
        User system = new User();
        system.setId(-1L);
        system.setUsername("系统");
        for (Clue clue : staleClues) {
            if (!clue.getRecycled()) {
                clueLogService.logRecycle(clue, system);
                clueLogService.logStatusChange(clue, system, clue.getStatus().getLabel(), "AUTO_RECYCLED");
            }
        }
    }
    
    private ClueDTO convertToDTO(Clue clue) {
        ClueDTO dto = new ClueDTO();
        BeanUtils.copyProperties(clue, dto);
        
        User userOwner = userService.getUserById(clue.getOwnerId());
        User userAssigner = userService.getUserById(clue.getAssignerId());
        if (userOwner != null) {
            dto.setOwnerName(userOwner.getUsername());
        }
        if (userAssigner != null) {
            dto.setAssignerName(userAssigner.getUsername()); 
        }
        
        if (clue.getStatus() != null) {
            dto.setStatusLabel(clue.getStatus().getLabel());
            dto.setStatus(clue.getStatus().name());
        }
        
        if (clue.getSource() != null) {
            dto.setSource(clue.getSource().name());
            dto.setSourceLabel(clue.getSource().getLabel());
        }
        
        if (clue.getPriority() != null) {
            dto.setPriority(clue.getPriority().name());
            dto.setPriorityLabel(clue.getPriority().getLabel());
        }
        
        var convertType = ClueConversionType.getByCode(clue.getConvertType());
        if(convertType != null) {
            dto.setConvertTypeLabel(convertType.getDescription());
        }

        return dto;
    }

    @Override
    public void followupClue(Long clueId, ClueFollowUpRequest request, Long currentUserId) {
        Clue clue = clueRepository.findById(clueId)
                .orElseThrow(() -> new RuntimeException("线索不存在"));
                
        String statusBefore = clue.getStatus().getLabel();
        clue.setStatus(ClueStatus.FOLLOW_UP);
        clue.setUpdateAt(System.currentTimeMillis());
        clue.setUpdateUserId(currentUserId);
        clue.setLastFollowUpTime(System.currentTimeMillis());
        clue.setNextFollowUpTime(request.getNextPlanTime());
        clue = clueRepository.save(clue);
        
        User operator = userService.getUserById(currentUserId);
        clueLogService.logFollowUp(clue, operator, "常规跟进");
        
        if (!statusBefore.equals(clue.getStatus().getLabel())) {
            clueLogService.logStatusChange(clue, operator, statusBefore, clue.getStatus().getLabel());
        }

        ClueFollowUp clueFollowUp = new ClueFollowUp();
        BeanUtils.copyProperties(request, clueFollowUp);
        clueFollowUp.setClueId(clueId);
        clueFollowUp.setCreateAt(System.currentTimeMillis());
        clueFollowUp.setCreateUserId(currentUserId);
        clueFollowUpService.addFollowUp(clueId, request, currentUserId);
    }

    @Override
    public void acceptClue(Long id, Long currentUserId) {
        Clue clue = clueRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("线索不存在"));

        if (clue.getOwnerId() == null) {
            throw new RuntimeException("线索未被分配，无法接受");
        }
        if(!clue.getOwnerId().equals(currentUserId)||!clue.getStatus().equals(ClueStatus.ASSIGNED)){
            throw new RuntimeException("线索状态不正确，无法接受");
        }
        clue.setStatus(ClueStatus.ACCEPTED);
        clue.setUpdateAt(System.currentTimeMillis());
        clue.setUpdateUserId(currentUserId);
        clueRepository.save(clue);

        User operator = userService.getUserById(currentUserId);
        clueLogService.logAccept(clue, operator);
        clueLogService.logStatusChange(clue, operator, clue.getStatus().getLabel(), "ACCEPTED");
    }
}